package cn.magicman.demo;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用ConcurrentHashMap实现高并发缓存（解决一致性问题）
 * 功能：原子性加载缓存（避免重复查询数据库），支持高并发读写
 */
public class ConcurrentCache {
    // 缓存容器（ConcurrentHashMap：线程安全，支持高并发读写）
    private final Map<String, Object> cache = new ConcurrentHashMap<>();

    /**
     * 获取缓存值（原子操作）
     * 逻辑：如果键不存在，调用loadDataFromDatabase加载数据并放入缓存（仅执行一次）
     * @param key 缓存键（非null）
     * @return 缓存值（非null）
     */
    public Object get(String key) {
        // computeIfAbsent：原子性检查键是否存在，不存在则执行加载函数
        // 加载函数（第二个参数）仅在键不存在时执行，且仅执行一次（多线程竞争时）
        return cache.computeIfAbsent(key, this::loadDataFromDatabase);
    }

    /**
     * 模拟从数据库加载数据（耗时操作）
     * @param key 缓存键
     * @return 加载的数据（非null）
     */
    private Object loadDataFromDatabase(String key) {
        // 模拟数据库查询耗时（500ms）
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("加载数据被中断", e);
        }
        // 打印加载日志（验证是否仅执行一次）
        System.out.println("【数据库加载】key=" + key + "，线程=" + Thread.currentThread().getName());
        // 返回模拟数据（比如键的大写形式）
        return key.toUpperCase();
    }

    // ---------------------- 测试用例 ----------------------
    public static void main(String[] args) throws InterruptedException {
        ConcurrentCache cache = new ConcurrentCache();
        int threadCount = 5; // 模拟5个并发线程
        String testKey = "testKey"; // 测试键

        // 1. 创建5个线程，同时获取同一个键
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                Object value = cache.get(testKey);
                System.out.println("【线程" + Thread.currentThread().getName() + "】获取到值：" + value);
            }, "Thread-" + i);
        }

        // 2. 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }

        // 3. 等待所有线程结束
        for (Thread thread : threads) {
            thread.join();
        }

        // 4. 打印缓存内容（验证是否加载成功）
        System.out.println("【缓存内容】" + cache);
    }
}